package cn.wolfcode.wolf2w.business.service.impl;

import cn.wolfcode.wolf2w.business.api.RemoteDestinationService;
import cn.wolfcode.wolf2w.business.api.domain.*;
import cn.wolfcode.wolf2w.business.mapper.*;
import cn.wolfcode.wolf2w.business.query.StrategyQuery;
import cn.wolfcode.wolf2w.business.service.IStrategyService;
import cn.wolfcode.wolf2w.business.util.DateUtil;
import cn.wolfcode.wolf2w.business.vo.ThemeVO;
import cn.wolfcode.wolf2w.common.core.constant.SecurityConstants;
import cn.wolfcode.wolf2w.common.core.context.SecurityContextHolder;
import cn.wolfcode.wolf2w.common.core.domain.R;
import cn.wolfcode.wolf2w.common.redis.service.RedisService;
import cn.wolfcode.wolf2w.common.redis.util.RedisKeys;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 攻略Service业务层处理
 *
 * @author zty
 * @date 2025-09-16
 */

@Service
@Transactional
public class StrategyServiceImpl extends ServiceImpl<StrategyMapper, Strategy> implements IStrategyService {
    @Resource
    private StrategyRankMapper strategyRankMapper;
    @Resource
    private StrategyConditionMapper strategyConditionMapper;
    @Resource
    private StrategyThemeMapper strategyThemeMapper;
    @Resource
    private StrategyCatalogMapper strategyCatalogMapper;
    @Resource
    private RemoteDestinationService remoteDestinationService;
    @Resource
    private StrategyContentMapper strategyContentMapper;
    @Resource
    private RedisService redisService;

    /*分页查询+多条件过滤（国内外/主题/无条件）*/
    @Override
    public IPage<Strategy> queryPage(StrategyQuery qo, String orderBy, int currentPage, Integer refId, Integer type) {
        // 设置当前页码
        qo.setCurrentPage(currentPage);
        IPage<Strategy> page = new Page<>(qo.getCurrentPage(), qo.getPageSize());
        if (refId == null) {

            return lambdaQuery()
                    .orderByDesc(Strategy::getViewnum)
                    .page(page);
        } else {
            if (type == 3) {
                return lambdaQuery()
                        .eq(Strategy::getThemeId, refId)
                        .orderByDesc(Strategy::getViewnum)
                        .page(page);
            } else {

                List<Strategy> list = lambdaQuery()
                        .eq(Strategy::getDestId, refId)
                        .orderByDesc(Strategy::getViewnum)
                        .list();
                page.setRecords(list);
                page.setTotal(list.size());
                return page;
            }
        }
    }

    /*定时添加攻略方法：
     * 按照type区分国内外
     * 添加到StrategyCondition表
     * 按照主题区分
     * */
    @Override
    public void statisCondition() {
        QueryWrapper<Strategy> queryWrapper = new QueryWrapper<>();
        //type=1,isboard=1--国外
        queryWrapper.select("min(dest_id) refid ,dest_name name,count(dest_name) count");
        queryWrapper.eq("isabroad", 1);
        queryWrapper.groupBy(" dest_name");
        List<Map<String, Object>> aboardList = baseMapper.selectMaps(queryWrapper);


        queryWrapper.clear();
        //type=2,isboard=0--国内
        queryWrapper.select("min(dest_id)  refid,dest_name name,count(dest_name) count");
        queryWrapper.eq("isabroad", 0);
        queryWrapper.groupBy("dest_name");
        List<Map<String, Object>> chinaList = baseMapper.selectMaps(queryWrapper);


        queryWrapper.clear();
        //type=3--主题：不区分国内和国外
        queryWrapper.select("min(theme_id)  refid,theme_name name,count(theme_name) count");
        queryWrapper.groupBy("theme_name");
        List<Map<String, Object>> themeList = baseMapper.selectMaps(queryWrapper);

        Date now = new Date();
        addCondition(aboardList, now, 1);
        addCondition(chinaList, now, 2);
        addCondition(themeList, now, 3);

    }

    /*封装定时添加攻略方法*/
    private void addCondition(List<Map<String, Object>> list, Date now, long type) {
        StrategyCondition condition = new StrategyCondition();

        for (Map<String, Object> map : list) {

            condition.setName(map.get("name").toString());
            Long count = Long.valueOf(map.get("count").toString());
            condition.setCount(count);
            Long refid = Long.valueOf(map.get("refid").toString());
            condition.setRefid(refid);
            condition.setType(type);
            condition.setStatisTime(now);
            strategyConditionMapper.insert(condition);
        }
    }

    /*新增攻略方法--admin*/
    @Override
    public void insert(Strategy strategy) {
        StrategyTheme strategyTheme = strategyThemeMapper.selectById(strategy.getThemeId());
        strategy.setThemeName(strategyTheme.getName());
        StrategyCatalog strategyCatalog = strategyCatalogMapper.selectById(strategy.getCatalogId());
        strategy.setCatalogName(strategyCatalog.getName());
        strategy.setDestId(strategyCatalog.getDestId());
        strategy.setDestName(strategyCatalog.getDestName());
        strategy.setCreateTime(new Date());
        strategy.setViewnum(0l);
        strategy.setReplynum(0l);
        strategy.setFavornum(0l);
        strategy.setSharenum(0l);
        strategy.setThumbsupnum(0l);
        Boolean isabroad = remoteDestinationService.isabroad(strategy.getDestId(), SecurityConstants.INNER).getData();
        strategy.setIsabroad(isabroad ? 1L : 0L);
        baseMapper.insert(strategy);
        Long id = strategy.getId();
        strategy.getContent().setId(id);
        strategyContentMapper.insert(strategy.getContent());


    }

    /*阅读量++*/
    @Override
    public Map<String, Object> viewnumIncr(Long sid) {
        String key = strategyHashInit(sid);
        /*阅读量加一*/
        redisService.incrementCacheMapValue(key, "viewnum", 1);
        /*重查一下，防止高并发情景导致数据不对*/
        Map<String, Object> cacheMap = redisService.getCacheMap(key);
        return cacheMap;


    }
    /*评论量++*/

    @Override
    public Map<String, Object> replynumIncr(Long sid) {
        String key = strategyHashInit(sid);
        /*评论量加一*/
        redisService.incrementCacheMapValue(key, "replynum", 1);
        /*重查一下，防止高并发情景导致数据不对*/
        Map<String, Object> cacheMap = redisService.getCacheMap(key);
        return cacheMap;

    }

    /*收藏功能*/
    @Override
    public Map<String, Object> favor(Long sid) {
        Long userId = SecurityContextHolder.getUserId();
        //当前用户收藏的攻略的key
        String redisKeys = RedisKeys.USER_STRATEGY_FAVOR.join(userId.toString());
        //判断redis中是否已经存在此用户收藏的攻略
        if (!redisService.hasKey(redisKeys)) {
            //redis中没查到改用户对应的key--即用户没有收藏过
            Set<Long> sids = new HashSet<>();
            //添加一个-1防止集合被清除：-1不影响
            sids.add(-1l);
            redisService.setCacheSet(redisKeys, sids);
            /*redis中集合的特性：
             * 集合中必须至少有一个数据，如果没有数据，会被清掉
             * */
        }
        //存取统计数字hashkey
        String hashKey = RedisKeys.STRATGE_STATIS_HASH.join(sid.toString());
        //添加一个布尔类型的变量
        boolean isFavor = false;
        //判断redis中此用户是否收藏过此攻略
        if (redisService.isCacheSetContains(redisKeys, sid)) {
            //存在此攻略--取消收藏
            //减一
            redisService.incrementCacheMapValue(hashKey, "favornum", -1);
            //移除uid中对应的sid
            redisService.deleteCacheSetValue(redisKeys, sid);

        } else {
            //不存在此攻略--添加收藏
            //加一
            redisService.incrementCacheMapValue(hashKey, "favornum", 1);
            //添加uid中对应的sid
            redisService.addCacheSetValue(redisKeys, sid);
            //设置收藏功能为true
            isFavor = true;
        }
        //给前端返回统计的数量
        Map<String, Object> cacheMap = redisService.getCacheMap(hashKey);
        //根据前端要求返回的结果
        cacheMap.put("result", isFavor);


        return cacheMap;
    }

    /*判断是否是用户收藏*/
    @Override
    public Boolean isUserFavor(Long uid, Long sid) {
        /*判断redis中是否有此用户收藏的攻略*/
        String redisKeys = RedisKeys.USER_STRATEGY_FAVOR.join(uid.toString());
        //判断redis中是否已经存在此用户收藏的攻略key--如果不存在创建一个 key--初始化
        if (!redisService.hasKey(redisKeys)) {
            //redis中没查到改用户对应的key--即用户没有收藏过
            Set<Long> sids = new HashSet<>();
            //添加一个-1防止集合被清除：-1不影响
            sids.add(-1l);
            redisService.setCacheSet(redisKeys, sids);
            /*redis中集合的特性：
             * 集合中必须至少有一个数据，如果没有数据，会被清掉
             * */
        }

        return redisService.isCacheSetContains(redisKeys, sid);

    }

    /*定时数据持久化*/
    @Override
    public void strategyStatisHashPersistence() {
        //去redis中获取StrategyStatisHash开头的key
        //创建条件
        String hashKey = RedisKeys.STRATGE_STATIS_HASH.join("*");
        //返回一个所有统计数字的key的数组
        Collection<String> keys = redisService.keys(hashKey);
        //判断集合不为空且里面有内容
        if (keys != null && !keys.isEmpty()) {
            //遍历集合
            for (String key : keys) {
                //文章对应的统计数字
                Map<String, Object> cacheMap = redisService.getCacheMap(key);
                //往数据库更新
                lambdaUpdate().eq(Strategy::getId, cacheMap.get("id"))//条件id
                        .set(Strategy::getViewnum, cacheMap.get("viewnum"))//浏览量
                        .set(Strategy::getReplynum, cacheMap.get("replynum"))//评论量
                        .set(Strategy::getFavornum, cacheMap.get("favornum"))//收藏量
                        .set(Strategy::getSharenum, cacheMap.get("sharenum"))//分享量
                        .set(Strategy::getThumbsupnum, cacheMap.get("thumbsupnum"))//点赞量
                        .update();//更新

            }
        }

    }
    /*点赞功能--一个账户每日上限三个*/

    @Override
    public Map<String, Object> thumbsup(Long sid) {
        //注入uid
        Long uid = SecurityContextHolder.getUserId();
        //拼key
        String key = RedisKeys.USER_STRATEGY_THUMBSUP.join(sid.toString(), uid.toString());
        //计算过期时间
        Date now = new Date();
        Date end= DateUtil.getLastMinuteOfDay(now);
        long time = DateUtil.getDateBetween(now, end);
        //判断redis中是否有key， 如果没有创建key设置值0
        if( ! redisService.hasKey(key)){
            redisService.setCacheObject(key, 0, time);
        }
        //点赞次数++
        Long ret = redisService.incrementCacheObjectValue(key, 1);

        String hashKey = RedisKeys.STRATGE_STATIS_HASH.join(sid.toString());

        //判断点赞次数，如果大于3，设置result为false
        boolean result = false;
        //如果小于等于3，设置result为true
        if(ret <= 3){
            result = true;
            redisService.incrementCacheMapValue(hashKey, "thumbsupnum", 1);
        }
        //查询统计数字map结构，并将result加入到map中

        Map<String, Object> cacheMap = redisService.getCacheMap(hashKey);
        cacheMap.put("result", result);
        //返回map
        return cacheMap;
    }

    @Override
    public List<Strategy> strategyTop5() {
        List<Strategy> list = lambdaQuery()
                .orderByDesc(Strategy::getViewnum)
                .last("limit 5").list();

        return list;
    }
   /* @Override
    public Map<String, Object> thumbsup(Long sid) {
        //获取当前用户id
        Long userId = SecurityContextHolder.getUserId();
        //创建sid的点赞
        String hashKey = RedisKeys.STRATGE_STATIS_HASH.join(sid.toString());
        //创建uid，sid点赞的key
        String userThumbsupKey = RedisKeys.USER_STRATEGY_THUMBSUP.join(userId.toString(), sid.toString());

        //判断redis中是否已经存在此用户点赞的攻略key--如果不存在创建一个 key--初始化
        if (!redisService.hasKey(userThumbsupKey)) {
            HashMap<String, Object> map = new HashMap<>();
            map.put("count", 1);
            redisService.setCacheMap(userThumbsupKey, map);

            //设置过期时间到次日零点
            int expireSeconds = (int) RedisKeys.getNextDayTime();
            redisService.expire(userThumbsupKey, expireSeconds);
        }
        //初始化map记录
        Map<String, Object> map;
        Map<String, Object> cacheMap = redisService.getCacheMap(userThumbsupKey);
        Integer count = (Integer) cacheMap.get("count");
        redisService.incrementCacheMapValue(userThumbsupKey, "count", 1);
        if (count > 3) {
            // Redis中。。加一
            map = redisService.getCacheMap(hashKey);
            map.put("result", false);
            throw new RuntimeException("您已超过每日点赞上限");


        } else {
            redisService.incrementCacheMapValue(hashKey, "thumbsupnum", 1);
            map = redisService.getCacheMap(hashKey);
            map.put("result", true);

        }

        return map;


    }*/

    /*初始化--缓存预热和容错机制*//*面试：注意如果出现击穿-缓存穿透--布隆过滤器--实现原理--三个Hash映射--询问mysql有没有*/
    private String strategyHashInit(Long sid) {
        /*存储当前攻略的key*/
        String key = RedisKeys.STRATGE_STATIS_HASH.join(sid.toString());
        if (!redisService.hasKey(key)) {
            /*当出现及特殊情况，redis服务器挂掉--查询mysql数据库*/
            Strategy strategy = this.getById(sid);
            Map<String, Object> map = new HashMap<>();
            map.put("viewnum", Integer.valueOf(strategy.getViewnum().toString()));
            /*数据库类型是Long数据带l，redis存储的时候带l，redis取数据带l可以正常识别，但是带l进行++操作会识别成字符串报错*/
            map.put("replynum", Integer.valueOf(strategy.getReplynum().toString()));
            map.put("sharenum", Integer.valueOf(strategy.getSharenum().toString()));
            map.put("favornum", Integer.valueOf(strategy.getFavornum().toString()));
            map.put("thumbsupnum", Integer.valueOf(strategy.getThumbsupnum().toString()));
            map.put("id", strategy.getId());
            redisService.setCacheMap(key, map);


        }
        return key;
    }

    /*目的地页获取自由行攻略TOP3--根据浏览量排序*/
    @Override
    public List<Strategy> strategyTop3(Long destId) {
        List<Strategy> list = lambdaQuery().eq(Strategy::getDestId, destId)
                .orderByDesc(Strategy::getViewnum)
                .last("limit 3").list();

        return list;
    }

    /*热门排行榜--数据定时更新*/
    @Override
    public void statisRank() {
        //国外--查询isabroad=1且按照浏览量降序排序限制最多十条
        /*select * from ta_strategy where isabroad=1 ORDER BY  viewnum DESC limit 10*/
        List<Strategy> abordList = lambdaQuery().eq(Strategy::getIsabroad, 1)
                .orderByDesc(Strategy::getViewnum)
                .last("limit 10")
                .list();
        // 国内--和上面的同理--只将isabroad=0
        /*select * from ta_strategy where isabroad=0 ORDER BY  viewnum DESC limit 10*/
        List<Strategy> chinaList = lambdaQuery().eq(Strategy::getIsabroad, 0)
                .orderByDesc(Strategy::getViewnum)
                .last("limit 10")
                .list();
        //热门--去掉是否是国外字段的判断即可
        /*select * from ta_strategy ORDER BY  viewnum DESC limit 10*/
        List<Strategy> Hotlist = lambdaQuery().orderByDesc(Strategy::getViewnum).last("limit 10").list();
        //获取当前时间
        Date now = new Date();
        //调用添加排行数据方法
        addRank(abordList, now, 1);
        addRank(chinaList, now, 2);
        addRank(Hotlist, now, 3);

    }

    /*  封装添加排行数据方法*/
    private void addRank(List<Strategy> list, Date now, long type) {
        //遍历传递进来的集合攻略对象
        for (Strategy strategy : list) {
            //创建排行对象
            StrategyRank rank = new StrategyRank();
            //分别给排行对象赋值
            rank.setId(strategy.getId());
            rank.setDestId(strategy.getDestId());
            rank.setDestName(strategy.getDestName());
            rank.setStrategyId(strategy.getDestId());
            rank.setStrategyTitle(strategy.getTitle());
            rank.setType(type);
            rank.setStatisTime(now);
            rank.setStatisnum(strategy.getViewnum());
            //添加排行数据
            strategyRankMapper.insert(rank);

        }

    }

    /*主题推荐*/
    @Override
    public List<ThemeVO> queryTheme() {
        QueryWrapper<Strategy> queryWrapper = new QueryWrapper<>();
        queryWrapper.groupBy("theme_name").select("theme_name, GROUP_CONCAT(DISTINCT dest_id) ids, GROUP_CONCAT(DISTINCT dest_name) names ");
        List<Map<String, Object>> list = baseMapper.selectMaps(queryWrapper);
        List<ThemeVO> themeVOS = new ArrayList<>();
        for (Map<String, Object> map : list) {
            String themeName = map.get("theme_name").toString();
            String ids = map.get("ids").toString();
            String names = map.get("names").toString();
            String[] idArr = ids.split(",");
            String[] nameArr = names.split(",");
            List<Destination> dests = new ArrayList<>();
            for (int i = 0; i < idArr.length; i++) {
                Long id = Long.valueOf(idArr[i]);
                String name = nameArr[i];
                Destination dest = new Destination();
                dest.setId(id);
                dest.setName(name);
                dests.add(dest);
            }
            ThemeVO themeVO = new ThemeVO(themeName, dests);
            themeVOS.add(themeVO);
        }
        return themeVOS;
    }


}
